package org.oimwrapper.connectors.common;

import Thor.API.*;
import Thor.API.Operations.*;
import java.util.*;

import org.oimwrapper.connectors.*;

/**
 * Executes a new instance of a process task for set of matching resource instances.<p>
 *
 * This task expects the following parameters, in addition to the standard
 * parameters in the {@link OIMTaskBase} class.
 * <ul>
 * <li><code>TaskName</code> The name of a the task to execute for each matching 
 *                           resource instance.</li>
 * <li><code>Threads</code> The number of concurrent threads to run to submit events to 
 *                          OIM.  By default, ten submitting threads are run.</li>
 * <li><code>Username</code> The username of the user that owns the matching resource 
 *                           instances.</li>
 * <li><code>ResourceObject<code> The name of the resource object for the matching 
 *                                resource instances.</li>
 * <li><code>AdditionalFilter</code> An SQL filter to further filter the list of 
 *                                   matching resources instances.</li>
 * </ul>
 */
public class OIMExecuteProvisioningTasks extends OIMTaskBase {

	/**
	 * True if a stop has been requested for this task.
	 */
	private boolean stop;
	
	/**
	 * The provisioning operation utility used by this task.
	 */
	private tcProvisioningOperationsIntf provOp;
	
	/**
	 * The task defition operation utility used by this task.
	 */
	private TaskDefinitionOperationsIntf taskDefOp;

	/**
	 * Default constructor.
	 */
	public OIMExecuteProvisioningTasks() {
		super("OIMWRAPPER.TASK");
		stop = false;
	}

	/**
	 * This method is called by the scheduler to execute the task instance.
	 */
	public void execute() {
		try {
			provOp = (tcProvisioningOperationsIntf) getUtility(tcProvisioningOperationsIntf.class);
			int maxWorkers = Integer.parseInt(getDefaultAttribute("Threads", "10"));
			String query = "select orc.orc_key from orc";
			query += " inner join oiu on orc.orc_key = oiu.orc_key";
			query += " inner join obi on oiu.obi_key = obi.obi_key";
			query += " where orc_status <> 'X'";
			long userKey = getOimUser(getDefaultAttribute("Username", ""));
			if (userKey != 0L) {
				query += " and oiu.usr_key = " + Long.toString(userKey);
			}
			long resourceKey = getResourceObject(getDefaultAttribute("ResourceObject", ""));
			if (resourceKey != 0L) {
				query += " and obi.obj_key = " + Long.toString(resourceKey);
			}
			String additionalFilter = getDefaultAttribute("AdditionalFilter", "");
			if (additionalFilter.length() > 0) {
				query += " and " + additionalFilter;
			}
			Map[] keys = getOimData(query);
			long[] result = new long[keys.length];
			for (int i = 0; !stop && i < result.length; i++) {
				startThread(maxWorkers, new worker(this, Long.parseLong((String) keys[i].get("Process Instance.Key"))));
			}
			waitForAllThreads();
		} catch (Exception e) {
			OIMExceptionHandler.handleException(logger, e, getClass());
		}
	}

	/**
	 * This method is called by the scheduler to stop a running task instance.
	 * 
	 * @return <code>true</code>
	 */
	public boolean stop() {
		logger.info(" ---- Stopping current task ---- ");
		stop = true;
		return stop;
	}
	
	/**
	 * Processes a single resource instance.
	 * 
	 * @param ProcessInstanceKey The process instance key to add the task to.
	 */
	private void processSingleInstance(long ProcessInstanceKey) {
		try {
			String TaskName = getCriticalAttribute("TaskName");
			Map searchFor = new HashMap();
			searchFor.put("Process Definition.Tasks.Task Name", TaskName);
			tcResultSet tasks = taskDefOp.getTaskDetail(ProcessInstanceKey, searchFor);
			if (tasks.getRowCount() == 1) {
				provOp.addProcessTaskInstance(tasks.getLongValue("Process Definition.Tasks.Key"), ProcessInstanceKey);
			}
		} catch (Exception e) {
			OIMExceptionHandler.handleException(logger, e, getClass());
		}
	}
	
	/**
	 * Worker Thread.
	 */
	private class worker extends OIMTaskThreadBase {
		
		/**
		 * The invoking task.
		 */
		private OIMExecuteProvisioningTasks task;
		
		/**
		 * The process instance key to add the task to.
		 */
		private long processInstanceKey;
		
		/**
		 * Default Constructor.
		 * 
		 * @param Task The invoking task.
		 * @param ProcessInstanceKey The process instance key to add the task to.
		 */
		private worker(OIMExecuteProvisioningTasks Task, long ProcessInstanceKey) {
			super(Task);
			task = Task;
			processInstanceKey = ProcessInstanceKey;
		}
		
		/**
		 * Processes a single task.
		 */
		public void runOimThread() {
			task.processSingleInstance(processInstanceKey);
		}
	}
}